#include "event/signal_event.hpp"

#include "event/loop.hpp"

namespace ibox::event {

// signo -> SignalSubscriber*, 信号的订阅者
std::unordered_map<int, std::set<SignalSubscriber *>> g_signals_subscribers;
std::mutex g_signal_lock; // 保护signal_ctxs用
std::unordered_map<int, SignalContext> g_signal_ctxs;

void SignalHandlerFunc(int signo, siginfo_t *siginfo, void *context) {
    // NOTE: 这里不能使用 LogXXX() 打印日志, 因为有死锁的风险
    // 信号处理函数中也不应该有锁相关的操作

    std::cout << "SignalHandlerFunc called!!!\n";

    const auto &this_signal_ctx = g_signal_ctxs[signo];

    // 先执行旧的信号
    const auto &old_handler = this_signal_ctx.old_handler;
    if (static_cast<bool>(old_handler.sa_flags & SA_SIGINFO)) {
        if (old_handler.sa_sigaction) {
            old_handler.sa_sigaction(signo, siginfo, context);
        }
    }
#if 0
    } else {
        if (SIG_ERR != old_handler.sa_handler
            && SIG_IGN != old_handler.sa_handler
            && SIG_DFL != old_handler.sa_handler) {
            old_handler.sa_handler(signo);
        }
    }
#endif
    // 再执行自己的
    for (int fd : this_signal_ctx.write_fds) {
        std::cout << "SignalHandlerFunc fd: " << fd << '\n';
        [[maybe_unused]] auto wsize = write(fd, &signo, sizeof(signo));
    }
}

bool EpollLoop::subscribe_signal(int signo, SignalSubscriber *who) {
    // 如果还没有创建对应的信号
    if (m_signal_read_fd == -1) {
        if (!create_pipe(m_signal_read_fd, m_signal_write_fd)) {
            return false;
        }

        m_signalfd_read_event = std::make_unique<EpollFdEvent>(
            this, m_signal_read_fd, FdEvent::ReadEvent, Event::Mode::Forever);
        m_signalfd_read_event->set_callback(
            [&]([[maybe_unused]] uint32_t events) {
                // 出现FdEvent::ReadEvent事件时触发on_signal回调函数
                // 那么由谁创建FdEvent::ReadEvent事件呢?
                // --> 在EpollFdEvent获取到read事件时创建
                on_signal();
            });
        m_signalfd_read_event->enable();
    }

    auto &this_signal_subscribers = g_signals_subscribers[signo];
    if (this_signal_subscribers.empty()) {
        // 如果本Loop没有监听该信号，则要去 signal_ctxs 中订阅
        std::unique_lock<std::mutex> lock(g_signal_lock);

        // 要禁止信号触发
        sigset_t new_sigmask, old_sigmask;
        sigfillset(&new_sigmask);
        sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask);

        // 设置退出后恢复信号
        SetScopeExitAction(
            [&] { sigprocmask(SIG_SETMASK, &old_sigmask, nullptr); });

        auto &this_signal_ctx = g_signal_ctxs[signo];
        if (this_signal_ctx.write_fds.empty()) {
            bool is_fail = false;
            struct sigaction new_handler;
            memset(&new_handler, 0, sizeof(new_handler));
            sigemptyset(&new_handler.sa_mask);
            new_handler.sa_sigaction = SignalHandlerFunc;
            new_handler.sa_flags = SA_SIGINFO;
            ssize_t ret = ::sigaction(
                signo, &new_handler, &this_signal_ctx.old_handler);
            is_fail = (ret == -1);
            if (is_fail) {
                g_signals_subscribers.erase(signo);
                // Q: 为什么这里要进行一次删除操作？
                // A:
                // 因为L63在访问g_signals_subscribers[signo]时，如果不存在，就会默认创建一个。
                //    创建的这个this_signal_subscribers一定是空的。可直接删除。
                // 注意：erase()操作之后，this_signal_subscribers
                // 是失效了的，不能再被访问

                if (g_signals_subscribers.empty()) {
                    // 本Loop已经没有任何SignalSubscriber订阅任何信号了
                    // 可以销毁信号相关的资源了
                    m_signalfd_read_event->disable();
                }
                std::cout << "install signal " << signo << " fail";
                return false;
            }
        }
        this_signal_ctx.write_fds.insert(m_signal_write_fd);
    }
    this_signal_subscribers.insert(who);

    return true;
}

bool EpollLoop::unsubscribe_signal(int signo, SignalSubscriber *who) {
    auto &this_signal_subscribers = g_signals_subscribers[signo];
    this_signal_subscribers.erase(who); // 将订阅信息删除
    if (!this_signal_subscribers
             .empty()) { // 检查本Loop中是否已经没有SignalSubscriber订阅该信号了
        return true; // 如果还有，就到此为止
    }

    // 如果本Loop已经没有SignalSubscriber订阅该信号了
    g_signals_subscribers.erase(signo); // 则将该信号的订阅记录表删除
    {
        std::unique_lock<std::mutex> lock{g_signal_lock};

        // 要禁止信号触发
        sigset_t new_sigmask, old_sigmask;
        sigfillset(&new_sigmask);
        sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask);

        // 设置退出后恢复信号
        SetScopeExitAction(
            [&] { sigprocmask(SIG_SETMASK, &old_sigmask, nullptr); });

        // 并将 _signal_ctxs_ 中的记录删除
        auto &this_signal_ctx = g_signal_ctxs[signo];
        this_signal_ctx.write_fds.erase(m_signal_write_fd);
        if (this_signal_ctx.write_fds.empty()) {
            // 并还原信号处理函数
            ::sigaction(signo, &this_signal_ctx.old_handler, nullptr);
            g_signal_ctxs.erase(signo);
        }
    }

    if (!g_signals_subscribers.empty()) {
        return true;
    }

    // 已经没有任何SignalSubscriber订阅任何信号了
    m_signalfd_read_event->disable();

    return true;
}

void EpollLoop::on_signal() const {
    while (m_signal_read_fd != -1) {
        int signo_array[10]; // 一次性读10个
        auto rsize = read(m_signal_read_fd, &signo_array, sizeof(signo_array));
        std::cout << "rsize: " << rsize << '\n';
        if (rsize > 0) {
            const auto num = rsize / sizeof(int);
            for (size_t i = 0; i < num; ++i) {
                int signo = signo_array[i];
                auto iter = g_signals_subscribers.find(signo);
                if (iter != g_signals_subscribers.end()) {
                    auto subscribers = iter->second;
                    for (auto *s : subscribers) {
                        s->on_signal(signo);
                    }
                }
            }
        } else {
            if (errno != EAGAIN) {
                std::cout << "errno: " << errno << '\n';
            }
            break;
        }
    }
}

bool SignalEvent::enable() {
    if (is_enabled()) {
        return true;
    }

    for (int signal : m_sigset) {
        m_loop->subscribe_signal(signal, this);
    }

    m_is_enabled = true;
    return true;
}

bool SignalEvent::disable() {
    if (!is_enabled()) {
        return true;
    }

    if (m_loop) {
        for (int signal : m_sigset) {
            m_loop->unsubscribe_signal(signal, this);
        }
    }

    m_is_enabled = false;
    return true;
}

// SignalEvent的回调函数
void SignalEvent::on_signal(int signo) {
    if (m_mode == Mode::Oneshot) {
        disable();
    }

    m_loop->before_handle_one_event();
    if (m_cb) {
        m_cb(signo);
    }
    m_loop->after_handle_one_event();
}
} // namespace ibox::event
